package com.xbongbong.saas.help.workflow;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.ConfigConstant;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.contract.pojo.dto.ContractUpdateWhenOutstockDTO;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.product.pojo.ProductSavePojo;
import com.xbongbong.saas.analytical.impl.ContractValidateAnalyticalServiceImpl;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.domain.entity.ContractProductEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.PurchaseBalanceEntity;
import com.xbongbong.saas.domain.entity.PurchaseProductEntity;
import com.xbongbong.saas.domain.entity.ext.ContractEntityExt;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.ContractOutstockEnum;
import com.xbongbong.saas.enums.business.OrderEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.dictionary.OrderPayMentStatusEnum;
import com.xbongbong.saas.enums.dictionary.OrderStatusEnum;
import com.xbongbong.saas.enums.product.ContractOutstockProductEnum;
import com.xbongbong.saas.help.SaasProductHelp;
import com.xbongbong.saas.model.ContractModel;
import com.xbongbong.saas.model.ContractProductModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.model.PurchaseBalanceModel;
import com.xbongbong.saas.model.PurchaseProductModel;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getDoubleOrDefaultFromFormData;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 *
 * @author 魏荣杰
 *
 */
@Component
@Slf4j
public class ContractHelp {

    private static final Logger LOG = LoggerFactory.getLogger(ContractHelp.class);

    @Resource
    private EsHelper esHelper;
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private ContractProductModel contractProductModel;
    @Resource
    private PaasAppModel paasAppModel;
    @Resource
    private ContractValidateAnalyticalServiceImpl contractAnalyticalService;
    @Resource
    private SaasProductHelp saasProductHelp;
    @Resource
    private ContractModel contractModel;
    @Resource
    private PurchaseBalanceModel purchaseBalanceModel;
    @Resource
    private PurchaseProductModel purchaseProductModel;

    public void updateContractCostWhenOutstock(ContractUpdateWhenOutstockDTO contractUpdateWhenOutstockDTO, ContractEntityExt contractEntityExt) throws XbbException {
        try {
            JSONObject outstockData = contractUpdateWhenOutstockDTO.getNewData();
            Long outstockId = contractUpdateWhenOutstockDTO.getNewPaasFormDataEntity().getId();
            JSONArray productArray = outstockData.getJSONArray(ContractOutstockEnum.PRODUCT.getAttr());
            String corpid = contractUpdateWhenOutstockDTO.getCorpid();
            Long contractId = contractEntityExt.getId();
            JSONObject contractData = contractEntityExt.getData();
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
            boolQueryBuilder.filter(termQuery("data." + OutstockEnum.TYPE.getAttr(), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
            boolQueryBuilder.filter(termQuery("data." + OutstockEnum.REF_ID.getAttr(), contractId));
            boolQueryBuilder.filter(termQuery("del", 0));
            boolQueryBuilder.mustNot(termQuery(FieldTypeEnum.DATAID.getAlias(), outstockId));
            boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> outstockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
            List<Long> dataIdIn = new ArrayList<>();
            dataIdIn.add(-1L);
            for (PaasFormDataEntityExt outstock : outstockList) {
                dataIdIn.add(outstock.getDataId());
            }
            // 根据出库单获取对应的产品
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("outstockIdIn", dataIdIn);
            List<OutstockProductEntity> outstockProductList = outstockProductModel.findEntitys(param);

            // 计算合同出库成本：SUM（已出库产品数量*出库产品成本）
            double realCost = 0D;
            boolean isOld = false;
            // 合并的出库产品数量MAP
            Map<Long, Double> outstockProductNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (OutstockProductEntity outstockProductEntity : outstockProductList) {
                if (Objects.isNull(outstockProductEntity.getRefProductId())) {
                    isOld = true;
                }
                Long productId = outstockProductEntity.getProductId();
                Double productNum = outstockProductEntity.getProductNum();
                productNum = productNum == null ? 0D : productNum;
                Double cost = outstockProductEntity.getCost();
                cost = cost == null ? 0D : cost;
                realCost = Arith.add(realCost, Arith.mul(productNum, cost));
                if (outstockProductNumMap.containsKey(productId)) {
                    outstockProductNumMap.put(productId, Arith.add(outstockProductNumMap.get(productId), productNum));
                } else {
                    outstockProductNumMap.put(productId, productNum);
                }
            }
            // 未当前产品数量
            Map<Long, Double> nowProductNumMap = new HashMap<>(productArray.size());
            // 当前出库单成本计算
            for (int i = 0; i < productArray.size(); i++) {
                JSONObject jsonObject = productArray.getJSONObject(i);
                Long productId = FastJsonHelper.getLongFromFormData(jsonObject, ContractOutstockProductEnum.PRODUCT.getAttr());
                Double productNum = getDoubleOrDefaultFromFormData(jsonObject, ContractOutstockProductEnum.NUM.getAttr(), 0D);
                Double productCost = getDoubleOrDefaultFromFormData(jsonObject, ContractOutstockProductEnum.COST.getAttr(), 0D);
                Long refProductId = jsonObject.getLong(BusinessConstant.REF_PRODUCT_ID);
                if (!isOld) {
                    if (nowProductNumMap.containsKey(refProductId)) {
                        nowProductNumMap.put(refProductId, Arith.add(productNum, nowProductNumMap.get(refProductId)));
                    } else {
                        nowProductNumMap.put(refProductId, productNum);
                    }
                }
                realCost = Arith.add(realCost, Arith.mul(productNum, productCost));
                if (outstockProductNumMap.containsKey(productId)) {
                    outstockProductNumMap.put(productId, Arith.add(outstockProductNumMap.get(productId), productNum));
                } else {
                    outstockProductNumMap.put(productId, productNum);
                }
            }

            List<ContractProductEntity> contractProductList = contractProductModel.getProductsByConId(contractId, corpid);
            // 算出合同中每种产品的平均价
            Map<Long, String> avgCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (ContractProductEntity contractProductEntity : contractProductList) {
                Long productId = contractProductEntity.getProductId();
                Double productNum = contractProductEntity.getProductNum() == null ? 0D : Math.abs(contractProductEntity.getProductNum());
                if (isOld) {
                    if (avgCostMap.containsKey(productId)) {
                        // map中已经有相同产品，需要计算平均值
                        String productInfo = avgCostMap.get(productId);
                        String[] split = productInfo.split(",");
                        Double preCost = StringUtil.toDouble((split[0]), 0D);
                        Double preNum = StringUtil.toDouble((split[1]), 0D);
                        Double nowCost = SaasProductHelp.calculateCost(preNum, preCost, productNum, contractProductEntity.getCost(), 1);
                        Double nowNum = Arith.add(preNum, productNum);
                        avgCostMap.put(productId, nowCost + "," + nowNum);
                    } else {
                        // 将产品id为key，合同产品平均价和数量拼接字符串为value存入map中
                        String productInfo = contractProductEntity.getCost() + "," + productNum;
                        avgCostMap.put(productId, productInfo);
                    }
                } else if (nowProductNumMap.containsKey(contractProductEntity.getId())) {
                    // 分行已出库数量计算
                    contractProductEntity.setContractOutstockNum(Arith.add(contractProductEntity.getContractOutstockNum() == null ? 0D : contractProductEntity.getContractOutstockNum(), nowProductNumMap.get(contractProductEntity.getId())));
                }
            }
            double estimateCost = 0D;
            if (isOld) {
                // 老数据合同预估成本计算
                for (ContractProductEntity contractProductEntity : contractProductList) {
                    String productInfo = avgCostMap.get(contractProductEntity.getProductId());
                    if (StringUtils.isEmpty(productInfo)) {
                        continue;
                    }
                    String[] productInfos = productInfo.split(",");
                    if (outstockProductNumMap.containsKey(contractProductEntity.getProductId())) {
                        if (Arith.sub(contractProductEntity.getProductNum(), outstockProductNumMap.get(contractProductEntity.getProductId())) > 0) {
                            estimateCost = Arith.add(estimateCost, Arith.mul(Arith.sub(contractProductEntity.getProductNum(), outstockProductNumMap.get(contractProductEntity.getProductId())), StringUtil.toDouble((productInfos[0]), 0D)));
                        }
                    } else {
                        estimateCost = Arith.add(estimateCost, Arith.mul(contractProductEntity.getProductNum(), StringUtil.toDouble((productInfos[0]), 0D)));
                    }
                }
            } else {
                // 新数据合同预估成本计算
                for (ContractProductEntity contractProductEntity : contractProductList) {
                    if (Arith.sub(contractProductEntity.getProductNum(), contractProductEntity.getContractOutstockNum()) > 0) {
                        estimateCost = Arith.add(estimateCost, Arith.mul(contractProductEntity.getCost(), Arith.sub(contractProductEntity.getProductNum(), contractProductEntity.getContractOutstockNum())));
                    }
                }
            }

            // 计算退货退款的因素（客户可能在出库之前已经退过款）
            Boolean isJxcUse = paasAppModel.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
            Map<Long, Double> contractIdAndCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, Double> allContractCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            contractAnalyticalService.calculateContractRefundAmount(corpid, contractId, isJxcUse, contractIdAndCostMap, allContractCostMap);

            //Double surchargeAmount =  calculateSurcharge(contractSurchargeList);
            //2018/6/22 凤娇优惠不计入成本
            Double surchargeAmount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.OTHER_EXPENSE.getAttr(), BasicConstant.ZERO_DOUBLE);
            // 合同成本 = 合同产品成本 - 退货产品成本（入库单的产品成本：新数据用入库单的成本，旧数据用出库单的平均成本）+ 其他费用
            Double contractCost;
            if (contractIdAndCostMap.containsKey(contractId)) {
                Double cost = contractIdAndCostMap.get(contractId) == null ? BasicConstant.ZERO_DOUBLE : contractIdAndCostMap.get(contractId);
                contractCost = Arith.sub(Arith.add(Arith.add(estimateCost, realCost), surchargeAmount), cost);
            } else {
                contractCost = Arith.add(Arith.add(estimateCost, realCost), surchargeAmount);
            }

            contractData.put(ContractEnum.ESTIMATE_COST.getAttr(), Arith.round(estimateCost, 2));
            contractData.put(ContractEnum.REAL_COST.getAttr(), Arith.round(realCost, 2));
            contractData.put(ContractEnum.CONTRACT_COST.getAttr(), Arith.round(contractCost, 2));
            Double amount = contractData.getDouble(ContractEnum.AMOUNT.getAttr());
            amount = Objects.isNull(amount) ? 0D : amount;
            //合同毛利 = 合同金额 - 退货金额（关联计算）- 合同成本（同上）
            Double grossProfit;
            if (allContractCostMap.containsKey(contractId)) {
                Double cost = allContractCostMap.get(contractId) == null ? BasicConstant.ZERO_DOUBLE : allContractCostMap.get(contractId);
                grossProfit = Arith.sub(Arith.sub(amount, contractCost), cost);
            } else {
                grossProfit = Arith.sub(amount, contractCost);
            }
            contractData.put(ContractEnum.GROSS_PROFIT.getAttr(), Arith.round(grossProfit, 2));
            //合同毛利率
            if (Double.compare(amount, 0D) != 0) {
                contractData.put(ContractEnum.GROSS_PROFIT_RATE.getAttr(), Arith.round(Arith.div(grossProfit, amount), 4));
            } else {
                contractData.put(ContractEnum.GROSS_PROFIT_RATE.getAttr(), 0D);
            }
            // 现金毛利
            Double finishAmount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
            double cashProfit = Arith.sub(finishAmount, contractCost);
            contractData.put(ContractEnum.CASH_PROFIT.getAttr(), cashProfit);
            if (Double.compare(finishAmount, BasicConstant.ZERO_DOUBLE) != 0) {
                contractData.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), Arith.round(Arith.div(cashProfit, finishAmount), 2));
            } else {
                contractData.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), BasicConstant.ZERO_DOUBLE);
            }
            //21880 【成功部】编辑出库单填写物流公司，实际的合同订单的界面没有生成更新后的出库单的物流公司
            //编辑出库单时修改对应合同物流公司信息
            if (Objects.nonNull(outstockData.get(OutstockEnum.EXPRESS_NAME.getAttr()))) {
                contractData.put(ContractEnum.EXPRESS_NAME.getAttr(), outstockData.get(OutstockEnum.EXPRESS_NAME.getAttr()));
            }
        } catch (Exception e) {
            log.error("contractServiceImpl.updateContractCostWhenOutstock 出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }


    public boolean judgeContractAllOutBound(Long outstockId, Long contractId, String corpid, List<ProductSavePojo> productSavePojoList) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("contractId", contractId);
        param.put("type", XbbRefTypeEnum.CONTRACT.getCode());
        param.put("del", 0);
        // 拿到合同的所有产品
        List<ContractProductEntity> contractProductEntityList = contractProductModel.findEntitys(param);
        Map<Long, Double> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> outstockNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        boolean needMerge = false;
        for (ProductSavePojo productSavePojo : productSavePojoList) {
            if (Objects.isNull(productSavePojo.getRefProductId())) {
                needMerge = true;
                break;
            }
        }
        for (ProductSavePojo productSavePojo : productSavePojoList) {
            if (needMerge) {
                Double num = outstockNumMap.getOrDefault(productSavePojo.getId(), 0D);
                num = Arith.add(num, productSavePojo.getNum());
                outstockNumMap.put(productSavePojo.getId(), num);
            } else {
                Double num = outstockNumMap.getOrDefault(productSavePojo.getRefProductId(), 0D);
                num = Arith.add(num, productSavePojo.getNum());
                outstockNumMap.put(productSavePojo.getRefProductId(), num);
            }
        }
        List<ContractProductEntity> updateList = new ArrayList<>();

        //新数据逻辑
        if (!needMerge) {
            boolean isAllOutBound = true;
            for (ContractProductEntity entity : contractProductEntityList) {
                Long key = entity.getId();
                if (outstockNumMap.containsKey(key)) {
                    // 剩余未出
                    Double unStockNum = Arith.sub(entity.getProductNum(), entity.getOutstockNum());
                    // 当前出库
                    Double amendNum = outstockNumMap.get(key);
                    // 当前应出，包含超发的情况
                    Double outstockNum = amendNum;
                    entity.setOutstockNum(outstockNum);
                    // 剩余未出
                    amendNum = Arith.sub(unStockNum, amendNum) < 0 ? 0 : Arith.sub(unStockNum, amendNum);
                    if (amendNum > 0) {
                        isAllOutBound = false;
                    }
                    entity.setUpdateTime(DateTimeUtil.getInt());
                    updateList.add(entity);
                    entity.setContractOutstockNum(outstockNum);
                } else {
                    //该合同产品不在出库单中，合同肯定没出完
                    if (Arith.sub(entity.getProductNum(), entity.getOutstockNum()) > 0) {
                        isAllOutBound = false;
                    }
                }
            }
            if (!updateList.isEmpty()) {
                contractProductModel.updateOutStock(updateList, corpid);
            }
            return isAllOutBound;
        } else {
            //老数据逻辑
            for (ContractProductEntity entity : contractProductEntityList) {
                Long key = entity.getProductId();

                Double num = contractProductMap.getOrDefault(key, 0D);
                num = Arith.add(num, entity.getProductNum());
                contractProductMap.put(key, num);
                if (outstockNumMap.containsKey(key)) {
                    // 剩余未出
                    Double unStockNum = Arith.sub(entity.getProductNum(), entity.getOutstockNum());
                    // 当前出库
                    Double amendNum = outstockNumMap.get(key);

                    Double outstockNum;
                    Double outNum = Arith.sub(amendNum, unStockNum);
                    if (outNum > 0) {
                        outstockNum = entity.getProductNum();
                        outstockNumMap.put(key, outNum);
                    } else {
                        outstockNum = Arith.add(entity.getOutstockNum(), amendNum);
                        outstockNumMap.remove(key);
                    }
                    entity.setOutstockNum(outstockNum);
                    entity.setUpdateTime(DateTimeUtil.getInt());
                    entity.setContractOutstockNum(outstockNum);
                    updateList.add(entity);
                }
            }
            if (!updateList.isEmpty()) {
                contractProductModel.updateBatchImmediately(updateList, corpid);
            }
            //老数据需要减去其他入库产品的产品
            return saasProductHelp.judgeAllOutBound(contractProductMap, contractId, OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode(), corpid, productSavePojoList, outstockId);
        }

    }

    /**
     * 判断已收金额是否已经超过合同内的未收金额
     *
     * @param distributorMark
     * @param paymentSheetTypeEnum
     * @param corpid
     * @param contractIdToAmount
     * @param contractIds
     */
    public void checkContract(Integer distributorMark, PaymentSheetTypeEnum paymentSheetTypeEnum, String corpid, Map<Long, Double> contractIdToAmount, Set<Long> contractIds) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("idIn", contractIds);
        List<ContractEntityExt> contracts = contractModel.findEntitys(param);
        if (Objects.isNull(contracts)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        boolean isRedFlag = PaymentSheetTypeEnum.isRed(paymentSheetTypeEnum.getCode());
        boolean isBadFlag = PaymentSheetTypeEnum.isBad(paymentSheetTypeEnum.getCode());
        for (ContractEntityExt contractEntityExt : contracts) {
            String name = contractEntityExt.getSerialNo();
            if (Objects.equals(contractEntityExt.getDel(), DelEnum.DELETE.getDel())) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208032, PaymentErrorCodeEnum.API_ERROR_208032.getMsg(), I18nMessageUtil.getMessage(CommonConstant.CONTRACT), name);
            }
            Long contractId = contractEntityExt.getId();
            Double value = contractIdToAmount.getOrDefault(contractId, 0D);
            if (value == 0) {
                continue;
            }
            Double tempAmount = value;
            if (isRedFlag) {
                //红冲的，先把红冲金额取正数，然后判断红冲金额是否小于已收
                tempAmount = Math.abs(value);
            }
            JSONObject data = contractEntityExt.getData();
            Double finishMoney = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
            Double unAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.UN_FINISH_AMOUNT.getAttr(), 0D);
            //校验填充的金额是否超过合同的已收/未收（根据是否红冲判断不同）
            if (isRedFlag) {
                //红冲，限制红冲金额tempAmount小于等于合同已收金额finishMoney
                if (Arith.sub(tempAmount, finishMoney) > ConfigConstant.amountAccuracy) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_RED, I18nMessageUtil.getMessage(CommonConstant.CONTRACT) + name);
                }
            }else if (Arith.sub(value, unAmount) > ConfigConstant.amountAccuracy) {
                //普通回款、坏账，限制回款金额小于等于合同未收金额
                String msg = I18nMessageUtil.getMessage(ErrorMessageConstant.DETAIL_BEYOND_AMOUNT);
                if (isBadFlag) {
                    msg = I18nMessageUtil.getMessage(ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_BAD);
                }
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, msg, I18nMessageUtil.getMessage(CommonConstant.CONTRACT), name);
            }
        }
    }

    /**
     * 新增回款单时，对合同的条件封装
     *  迁移从com.xbongbong.saas.service.ContractService#handleContract(java.lang.Integer, com.xbongbong.paas.enums.PaymentSheetTypeEnum, java.lang.String, java.util.Map, java.util.Set)
     *
     * @param distributorMark
     * @param paymentSheetTypeEnum
     * @param corpid
     * @param contractIdToAmount
     * @param contractIds
     * @return
     * @throws XbbException
     */
    public List<UpdateDataEntity> handleContract(Integer distributorMark, PaymentSheetTypeEnum paymentSheetTypeEnum, String corpid, Map<Long, Double> contractIdToAmount, Set<Long> contractIds) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("idIn", contractIds);
        List<ContractEntityExt> contracts = contractModel.findEntitys(param);
        boolean isRedFlag = PaymentSheetTypeEnum.isRed(paymentSheetTypeEnum.getCode());
        boolean isBadFlag = PaymentSheetTypeEnum.isBad(paymentSheetTypeEnum.getCode());
        List<UpdateDataEntity> updateList = new ArrayList<>();
        for (ContractEntityExt contractEntityExt : contracts) {
            Long contractId = contractEntityExt.getId();
            Double value = contractIdToAmount.getOrDefault(contractId, 0D);
            if (value == 0) {
                continue;
            }
            JSONObject data = contractEntityExt.getData();
            Double finishMoney = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
            //校验填充的金额是否超过合同的已收/未收（根据是否红冲判断不同）
            if (isRedFlag) {
                //红冲的，把金额置为负数。用tempAmount计算金额是否合法
                value = Arith.mul(value, -1);
            }
            JSONObject updateData = new JSONObject();
            /*
             处理合同毛利，及新的已收未收
             */
            //旧的现金毛利 = 已收金额 - 合同成本
            Double oldCashProfit = finishMoney - FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.CONTRACT_COST.getAttr(), 0D);
            Double newCashProfit;
            Double newFinishMoney;
            Double badAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.BAD_AMOUNT.getAttr(), 0D);
            Double amount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.AMOUNT.getAttr(), 0D);
            Double newUnFinishMoney;
            if (isBadFlag) {
                /*
                 坏账时：已收不变，合同总额不变，未收减少，坏账增加，收款比例不变（= 已收/总合同额）
                 */
                newFinishMoney = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
                Double oldUnFinishMoney = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.UN_FINISH_AMOUNT.getAttr(), 0D);
                newUnFinishMoney = Arith.sub(oldUnFinishMoney, value);
                //坏账增加
                Double newBadAmount = Arith.add(badAmount, value);
                //坏账，毛利不变
                newCashProfit = oldCashProfit;
                updateData.put(ContractEnum.BAD_AMOUNT.getAttr(), newBadAmount);
            } else {
                /*
                 普通、红冲回款时（红冲是负值，所以可以和普通回款一样处理方式）：已收增加，合同总额不变，未收减少，坏账不变-收款比例增加（= 已收/总合同额），毛利增加
                 */
                //此处直接add没问题：红冲时value是负数
                //注：为防止与原逻辑不一致，此处来回加减的代码参照旧代码内对各种金额的处理
                newFinishMoney = Arith.add(finishMoney, value);
                //合同未收 = 合同金额 - 合同已收 - 合同坏账
                newUnFinishMoney = Arith.sub(amount, newFinishMoney);
                newUnFinishMoney = Arith.sub(newUnFinishMoney, badAmount);
                updateData.put(ContractEnum.FINISH_AMOUNT.getAttr(), newFinishMoney);
                FastJsonHelper.handleAttrPercent(updateData, newFinishMoney, amount, ContractEnum.PAY_PERCENT.getAttr());
                newCashProfit = oldCashProfit + value;
            }
            String orderStatus = FastJsonHelper.getStringFromFormData(contractEntityExt.getData(), ContractEnum.STATUS.getAttr());
            if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                //订货单则还需要处理收款状态
                String orderPayMentStatus = OrderPayMentStatusEnum.getOrderPayMentStatus(amount, newUnFinishMoney);
                updateData.put(OrderEnum.RECEIVE_PAYMENT_STATUS.getAttr(), orderPayMentStatus);
                if (Objects.equals(orderStatus, OrderStatusEnum.PENDING_FINANCIAL_REVIEW.getCode())) {
                    updateData.put(OrderEnum.STATUS.getAttr(), OrderStatusEnum.DELIVERED.getCode());
                }

            }
            updateData.put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), newUnFinishMoney);
            updateData.put(ContractEnum.CASH_PROFIT.getAttr(), newCashProfit);
            FastJsonHelper.handleAttrPercent(updateData, newCashProfit, newFinishMoney, ContractEnum.CASH_PROFIT_RATE.getAttr());
            UpdateDataEntity updateDataEntity = ExplainUtil.getUpdateData(contractId, updateData, corpid);
            updateList.add(updateDataEntity);
        }
        return updateList;
    }

    /**
     * 新建回款单时，同步更新合同
     * @param corpid 公司id
     * @param updateList 要更新的合同list
     * @param errorMsg 错误信息
     * @throws XbbException
     * @author zcp
     * @date 19/11/25 025 11:09
     * @update 19/11/25 025 11:09
     * @since v1.0
     * @version v1.0
     */
    public void saveContract(String corpid, List<UpdateDataEntity> updateList, String errorMsg) throws XbbException {
        try {
            if (updateList.size() > 0) {
                contractModel.updateBatch(updateList, corpid);
            }
        } catch (Exception e) {
            LOG.error(errorMsg, e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    // 获取产品缺货数量
    public void handleWaitPurchaseNum(String corpid,List<ContractProductEntity> entityList,Set<Long> contractIdSet) throws XbbException{
        List<Long> contractProductIdList = new ArrayList<>();
        List<String> onlyKeyList = new ArrayList<>();
        List<Long> contractIdList = new ArrayList<>(contractIdSet);
        for (ContractProductEntity contractProductEntity : entityList) {
            Long id = contractProductEntity.getId();
            contractProductIdList.add(id);
            onlyKeyList.add(id + "_" + contractProductEntity.getProductId());
        }

        // 获取采购结存表所有数据
        List<PurchaseBalanceEntity> purchaseBalanceEntityList = new ArrayList<>();
        if (CollectionsUtil.isNotEmpty(onlyKeyList)) {
            purchaseBalanceEntityList = purchaseBalanceModel.findByOnlyKeys(corpid, onlyKeyList);
        }
        // 超发库存map key = contractid_productid  value=超发库存总量
        Map<String, Double> exceedStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (CollectionsUtil.isNotEmpty(purchaseBalanceEntityList)) {
            purchaseBalanceEntityList.forEach(item -> exceedStockMap.put(item.getOnlyKey(), item.getExceedStockNum()));
        }
        // 获取采购总量map
        List<PurchaseProductEntity> purchaseProductEntities = purchaseProductModel.getPurchaseByContract(corpid, contractIdList);
        // contractProductId_productId 该合同产品的采购总量
        Map<String, Double> purchaseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (CollectionsUtil.isNotEmpty(purchaseProductEntities)){
            for (PurchaseProductEntity entity : purchaseProductEntities) {
                if (!purchaseMap.containsKey(entity.getContractProductId() + "_" + entity.getProductId())){
                    purchaseMap.put(entity.getContractProductId() + "_" + entity.getProductId(), entity.getProductNum());
                }else {
                    // 如果包含了说明采购多个 num+=num
                    BigDecimal b1 = new BigDecimal(Double.toString(purchaseMap.get(entity.getContractProductId() + "_" + entity.getProductId())));
                    BigDecimal b2 = new BigDecimal(Double.toString(entity.getProductNum()));
                    purchaseMap.put(entity.getContractProductId() + "_" + entity.getProductId(),  b1.add(b2).doubleValue());
                }
            }
        }

        for (ContractProductEntity contractProductEntity : entityList) {
            if (Objects.nonNull(contractProductEntity)) {
                Long productId = contractProductEntity.getProductId();
                String onlyKey = contractProductEntity.getId() + "_" + productId;

                // 建议采购量 = 合同总量 - 采购总量 - 超发库存总量
                double purchaseSumNum = 0D;
                if (!purchaseMap.isEmpty() && purchaseMap.containsKey(onlyKey)) {
                    purchaseSumNum = purchaseMap.get(onlyKey);
                }
                // 超发库存总量
                double exceedNum = 0D;
                if (!exceedStockMap.isEmpty() && exceedStockMap.containsKey(onlyKey)) {
                    exceedNum = exceedStockMap.get(onlyKey);
                }
                // 新公式: 建议采购量 = 合同总量 - 采购总量 - 超发库存总量
                double shortageQuantity = Arith.sub(contractProductEntity.getProductNum(), Arith.add(purchaseSumNum, exceedNum));
                contractProductEntity.setPurchaseNum(shortageQuantity);
            }
        }
    }
}
